package solver

import (
	"log"
	"math/rand"
	"time"
)

func (g *Grid) MakeSolvedGrid() (ok bool) {
	w := log.Writer()
	log.SetOutput(Discard{})
	defer log.SetOutput(w)

	g.Input("")
	g.Init()
	g.Clearup()
	g.Backtrack()
	ok = g.Validate()
	return
}

func (g *Grid) removeCells() {
	w := log.Writer()
	log.SetOutput(Discard{})
	defer log.SetOutput(w)

	cellxys := make([][2]int, 0, 81)
	for i := 0; i < 9; i++ {
		for j := 0; j < 9; j++ {
			cellxys = append(cellxys, [2]int{i, j})
		}
	}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	r.Shuffle(len(cellxys), func(i, j int) {
		cellxys[i], cellxys[j] = cellxys[j], cellxys[i]
	})
	for _, xy := range cellxys {
		before := Grid(*g)
		g.cells[xy[0]][xy[1]].setValue(0)
		g.solvedCount--

		after := Grid(*g)
		//trial.Init()
		//trial.Clearup()
		//trial.prepareStrategies()
		//fmt.Println(len(trial.strategies), trial.strategyNames)
		g.Init()
		g.Clearup()
		ok := g.MBacktrack()
		//g.Solve()
		if !ok || !g.Validate() {
			*g = before
		} else {
			*g = after
		}

	}
}

func (g *Grid) GenerateGrid() string {
	if !g.MakeSolvedGrid() {
		return ""
	}
	g.removeCells()

	return g.Values()

}

func generate() string {
	g := &Grid{}
	ok := g.MakeSolvedGrid()
	if !ok {
		return ""
	}
	g.strategies = []Strategy{
		g.LastOneBox,
		g.LastOneRow,
		g.LastOneCol,
		g.HiddenSingleBox,
		//g.HiddenSingleRow,
		//g.HiddenSingleCol,
		//g.NakedSingle,
	}
	g.removeCells()
	//g.printSimply()
	//fmt.Printf("The cues=%v\n", g.solvedCount)
	return g.Values()
}

func makeZeros(s string) string {
	w := log.Writer()
	log.SetOutput(Discard{})
	defer log.SetOutput(w)

	cellNums := [81]int{}
	for i := 0; i < 81; i++ {
		cellNums[i] = i
	}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	r.Shuffle(len(cellNums), func(i, j int) {
		cellNums[i], cellNums[j] = cellNums[j], cellNums[i]
	})

	for _, cellNum := range cellNums {
		g := Grid{}
		in := []byte(s)
		in[cellNum] = '0'
		g.Input(string(in))
		g.Init()
		g.Clearup()
		g.strategies = []Strategy{
			g.LastOneBox,
			g.LastOneRow,
			g.LastOneCol,
			g.HiddenSingleBox,
			g.HiddenSingleRow,
			g.HiddenSingleCol,
			g.NakedSingle,
			g.Pointing,
			g.Claiming,
			g.HiddenPair,
			g.NakedPair,
			g.HiddenTriple,
			g.NakedTriple,
			g.HiddenQuandruple,
			g.NakedQuadruple,
			g.XWing,
			g.Swordfish,
			g.Jellyfish,
			g.FinnedXWing,
			g.FinnedSwordfish,
			g.FinnedJellyfish,
			g.Skycraper,
			g.TwoStringKite,
			g.TurbotFish,
			g.XYWing,
			g.XYZWing,
			g.WXYZWing,
			g.VWXYZWing,
			g.WWing,
			//g.UniqueRectangleType1,
			//g.UniqueRectangleType2A,
			//g.UniqueRectangleType2B,
			//g.UniqueRectangleType3A,
			//g.UniqueRectangleType3B,
			//g.UniqueRectangleType4A,
			//g.UniqueRectangleType4B,
			//g.UniqueRectangleType4C,
			//g.UniqueRectangleXYZWing,
			//g.UniqueRectangleWXYZWing,
			g.AvoidRectangleType1,
			g.AvoidRectangleType2A,
			g.AvoidRectangleType3A,
			g.AvoidRectangleType3B,
			g.AlmostLockedPair,
			g.AlmostLockedSubsetType2,
			g.AlmostLockedSubsetType3,
			//g.BinaryUniversalGrave1,
			//g.BinaryUniversalGrave2,
			//g.BinaryUniversalGraveN,
			//g.BinaryUniversalGrave3Naked,
			//g.BinaryUniversalGrave3Hidden,
			//g.BinaryUniversalGrave4,
			//g.BinaryUniversalGraveXZ,
			g.XYChain,
			g.XCycle1,
			g.XCycle2,
			g.XCycle3,
		}
		if !g.filledCorrenctly() {
			continue
		}

		g.Solve()
		if g.Validate() {
			//fmt.Println(s)
			s = string(in)
		}
	}
	return s
}

func makeUniqueSolution(s string) string {
	w := log.Writer()
	log.SetOutput(Discard{})
	defer log.SetOutput(w)

	cellNums := [81]int{}
	for i := 0; i < 81; i++ {
		cellNums[i] = i
	}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	r.Shuffle(len(cellNums), func(i, j int) {
		cellNums[i], cellNums[j] = cellNums[j], cellNums[i]
	})

	for _, cellNum := range cellNums {
		g := Grid{}
		in := []byte(s)
		in[cellNum] = '0'
		g.Input(string(in))
		g.Init()
		g.Clearup()
		if !g.filledCorrenctly() {
			continue
		}

		used := g.MBacktrack()
		if used && g.Validate() {
			//fmt.Println(s)
			s = string(in)
		}
	}
	return s
}

func makeUniquePuzzle(s string, setStrat func(*Grid)) string {
	w := log.Writer()
	log.SetOutput(Discard{})
	defer log.SetOutput(w)

	cellNums := [81]int{}
	for i := 0; i < 81; i++ {
		cellNums[i] = i
	}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	r.Shuffle(len(cellNums), func(i, j int) {
		cellNums[i], cellNums[j] = cellNums[j], cellNums[i]
	})

	g := Grid{}
	setStrat(&g)
	for _, cellNum := range cellNums {
		in := []byte(s)
		in[cellNum] = '0'
		g.Input(string(in))
		g.Init()
		g.Clearup()
		if !g.filledCorrenctly() {
			continue
		}
		//used := g.MBacktrack()
		g.Solve()
		if g.Validate() {
			s = string(in)
		}
	}

	return s
}
